iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 15
0

本文同步發表於: Sponge Records

由於我們的專案有設計會員系統,本文將會延續著上一篇繼續製作帳號登入的功能,
這裡一樣會將架構分為 controllers\accountsControllers.go 、 models\accounts.go

建立登入帳號功能的 controllers\accountsControllers.go

我們將會實作 controllers\accountsControllers.go 中的 Authenticate,透過這個函數先解讀 json 格式的請求,並且驗證正確性,驗證完成後 call models\accounts.go 做事

controllers\accountsControllers.go - var Authenticate

var Authenticate = func(w http.ResponseWriter, r *http.Request) {

	account := &models.Account{}
	err := json.NewDecoder(r.Body).Decode(account)
	//如果輸入的請求錯誤
	if err != nil {
		u.Respond(w, u.Message(false, "Invalid request"))
		return
	}

	//傳入 Email 與 Password
	resp := models.Login(account.Email, account.Password)
	u.Respond(w, resp)
}

增加了 var Authenticate 後程式碼的長相

package controllers

import (
	"net/http"
	"encoding/json"
	"golang-api/models"
	u "golang-api/utils"
)

var CreateAccount = func(w http.ResponseWriter, r *http.Request) {

	account := &models.Account{}
	err := json.NewDecoder(r.Body).Decode(account)
	//如果輸入的請求錯誤
	if err != nil {
		u.Respond(w, u.Message(false, "Invalid request"))
		return
	}

	resp := account.Create() //呼叫 accounts.go 透過傳入的資料創建帳號
	u.Respond(w, resp)
}

var Authenticate = func(w http.ResponseWriter, r *http.Request) {

	account := &models.Account{}
	err := json.NewDecoder(r.Body).Decode(account)
	//如果輸入的請求錯誤
	if err != nil {
		u.Respond(w, u.Message(false, "Invalid request"))
		return
	}

	//傳入 Email 與 Password
	resp := models.Login(account.Email, account.Password)
	u.Respond(w, resp)
}

建立登入帳號功能的 models\accounts.go

我們一樣將主要的邏輯放置在這裡,Login 將會接在 Create 之後

models\accounts.go - Login

func Login(email, password string) map[string]interface{} {

	account := &Account{}
    //帳號驗證
	err := GetDB().Table("accounts").Where("email = ?", email).First(account).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return u.Message(false, "Email address not found")
		}
		return u.Message(false, "Connection error")
	}

    //密碼驗證
	err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password))
	if err != nil && err == bcrypt.ErrMismatchedHashAndPassword { 
		return u.Message(false, "Invalid login credentials")
	}	

	account.Password = ""	
    
    //創建 token
	tk := &Token{UserId: account.ID}
	token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
	tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
	account.Token = tokenString 

	resp := u.Message(true, "Logged In")
	resp["account"] = account
	return resp
}

增加了 Login 後程式碼的長相

package models

import (
	u "golang-api/utils" // 開頭的 u 代表用 u 來引用他
	"os"
	"strings"
	"github.com/dgrijalva/jwt-go"
	"github.com/jinzhu/gorm"
	"golang.org/x/crypto/bcrypt"
)


//JWT 結構
type Token struct {
	UserId uint
	jwt.StandardClaims
}

//帳號紀錄結構
type Account struct {
	gorm.Model
	Email    string `json:"email"`
	Password string `json:"password"`
	Token    string `json:"token";sql:"-"`
}

//驗證傳入訊息
func (account *Account) Validate() (map[string]interface{}, bool) {

	//如果傳入的 email 沒有含 @,回傳格式錯誤
	if !strings.Contains(account.Email, "@") {
		return u.Message(false, "Email format error"), false
	}

	if len(account.Password) < 6 {
		return u.Message(false, "Password format error"), false
	}

	//認證 Email 是否存在
	temp := &Account{}

	err := GetDB().Table("accounts").Where("email = ?", account.Email).First(temp).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return u.Message(false, "Connection error"), false
	}
	if temp.Email != "" {
		return u.Message(false, "Email has been used"), false
	}

	return u.Message(false, "Requirement passed"), true
}

func (account *Account) Create() map[string]interface{} {

	if resp, ok := account.Validate(); !ok {
		return resp
	}

	hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(account.Password), bcrypt.DefaultCost)
	account.Password = string(hashedPassword)

	GetDB().Create(account)

	if account.ID <= 0 {
		return u.Message(false, "Failed to create account")
	}

	tk := &Token{UserId: account.ID}
	token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
	tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
	account.Token = tokenString

	account.Password = "" 

	response := u.Message(true, "Account has been created")
	response["account"] = account
	return response
}

func Login(email, password string) map[string]interface{} {

	account := &Account{}
    //帳號驗證
	err := GetDB().Table("accounts").Where("email = ?", email).First(account).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return u.Message(false, "Email address not found")
		}
		return u.Message(false, "Connection error")
	}

    //密碼驗證
	err = bcrypt.CompareHashAndPassword([]byte(account.Password), []byte(password))
	if err != nil && err == bcrypt.ErrMismatchedHashAndPassword { 
		return u.Message(false, "Invalid login credentials")
	}	

	account.Password = ""	
    
    //創建 token
	tk := &Token{UserId: account.ID}
	token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
	tokenString, _ := token.SignedString([]byte(os.Getenv("token_password")))
	account.Token = tokenString 

	resp := u.Message(true, "Logged In")
	resp["account"] = account
	return resp
}

下回預告

透過本次的程式碼我們就完成了會員功能,下回將會進入報表功能的設計,首先會先以新增圖表開始進行


上一篇
[Day14] 實作創建帳號
下一篇
[Day16] 儲存圖片的 url
系列文
Golang - 從打造一個 API 開始學起30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言